package com.zhangkunhao.notes.dao;

import com.zhangkunhao.notes.annotation.TableField;
import com.zhangkunhao.notes.annotation.TableId;
import com.zhangkunhao.notes.annotation.TableName;
import com.zhangkunhao.notes.bean.PoolManager;
import com.zhangkunhao.notes.bean.PooledConnection;
import com.zhangkunhao.notes.util.MyPoolImpl;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public abstract class BaseDao<T> {

    /**
     * BaseDao的子类对象的泛型对象
     */
    private Class clazz;

    /**
     * 功能描述: 在创建BaseDao的子类对象时获取泛型的对象
     *
     * @param
     * @return
     * @author 张坤浩
     * @date
     */
    public BaseDao() {

        //得到继承BaseDao的子类对象的Class文件，比如UserDao
        Class<? extends BaseDao> aClass = this.getClass();
        //获取子类继承的父类对象并且带有具体的泛型，比如BaseDao<User>
        Type type = aClass.getGenericSuperclass();
        //强转为参数化类型, ParameterizedType是Type的子类
        ParameterizedType pt = (ParameterizedType) type;
        //获取参数化类型中实际泛型的定义,比如User,由于泛型支持多泛型，故是数组
        Type[] actualTypeArguments = pt.getActualTypeArguments();
        //继承BaseDao的子类中只有一个泛型，故
        clazz = (Class<? extends BaseDao>) actualTypeArguments[0];
    }

    /**
     * 功能描述: 抽取通用查询多个和单个的共同代码
     *
     * @param sql
     * @return
     * @author 张坤浩
     * @date
     */
    public List<T> find(String sql) {
        // 1.获取连接
        PooledConnection pooledConnection = PoolManager.getInstance().getPooledConnection();
        PreparedStatement ps = null;
        ResultSet rs = null;
        Connection conn = pooledConnection.getConnection();

        List<T> list = new ArrayList<>();
        try {
            // 2.获取PreparedStatement,用于执行sql
            ps = conn.prepareStatement(sql);

            // 3.执行sql,得到结果集(查询得到的数据表)
            rs = ps.executeQuery();

            // 4.获取结果集中的数据
            while (rs.next()) {
                //创建对象
                T t = (T) clazz.newInstance();
                //获取反射类的所有属性
                Field[] fields = clazz.getDeclaredFields();
                for (Field field : fields) {
                    //允许为属性赋值
                    field.setAccessible(true);
                    //获取主键注解
                    TableId tableId = field.getAnnotation(TableId.class);
                    if (tableId != null) {
                        //找到TableId注解标记的代表主键的变量并给新对象对应的属性赋值
                        field.set(t, rs.getObject(tableId.value()));
                    } else {
                        TableField tableField = field.getAnnotation(TableField.class);
                        if (tableField != null) {
                            //找到TableField标记的变量并给新对象对应的属性赋值
                            field.set(t, rs.getObject(tableField.value()));
                        } else {
                            //没有注解标记的变量，直接给新对象对应的属性赋值
                            field.set(t, rs.getObject(field.getName()));
                        }
                    }
                }
                //添加到集合容器
                list.add(t);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //关闭连接
            MyPoolImpl.free(pooledConnection, ps, rs);
        }
        return list;
    }

    /**
     * 功能描述: 根据id查询对象
     *
     * @param id ，params：自定义单条件的查询条件（可以是用户名等属性），可以覆盖默认的id查询，也可以啥属性都不输入
     * @return
     * @author 张坤浩
     * @date
     */
    public T findById(Object id, String... params) {
        //1.定义sql语句:String sql = "SELECT * FROM 表名 WHERE 条件;";
        StringBuffer sql = new StringBuffer("SELECT * FROM ");

        //获取表名
        String name = getTableName().name();
        sql.append(name + " where ");

        //获取反射类的所有属性
        Field[] fields = clazz.getDeclaredFields();
        //使用默认查询
        if (params != null && params.length == 0) {
            //拼接默认的根据id查询
            for (Field field : fields) {
                field.setAccessible(true);
                //获取主键id的注解
                TableId tableId = field.getAnnotation(TableId.class);
                if (tableId != null) {
                    //找到被TableId标记的成员变量
                    sql.append(tableId.value() + "='" + id + "'");
                    break;
                }
            }
        } else {
            //拼接自定义的查询条件
            sql.append(params[0] + "='" + params[1] + "'");
        }
        //System.out.println(sql);
        List<T> list = find(sql.toString());
        if (list != null && list.size() > 0) {
            return list.get(0);
        }
        return null;
    }

    /**
     * 功能描述：获取类的注解并判断是否已经用注解标记类对应的表名
     *
     * @param
     * @return
     * @author 张坤浩
     * @date
     */
    private TableName getTableName() {
        //2.获取类的注解
        TableName tableName = (TableName) clazz.getAnnotation(TableName.class);

        //判断是否已经用注解标记类对应的表名
        if (tableName == null) {
            throw new RuntimeException("未使用TableName注解标记表名");
        }
        return tableName;
    }


    /**
     * 功能描述：
     *
     * @param params 查询所有的id条件，可以不加，默认是 id>0，自定义是id>params
     * @return
     * @author 张坤浩
     * @date
     */
    public List<T> findAll(String... params) {
        //1.定义sql语句: String sql = "SELECT * FROM 表名 WHERE 条件;";
        StringBuffer sql = new StringBuffer("SELECT * FROM ");

        //获取表名
        String name = getTableName().name();
        sql.append(name);

        //如果还有id限制条件的话，继续拼接
        if (params != null && params.length > 0) {
            sql.append(" WHERE " + params[0] + ">" + params[1]);
        }
        //System.out.println(sql);
        return find(sql.toString());
    }

    /**
     * 功能描述
     *
     * @param id
     * @param params 删除的条件，可以不更改为其他自定义条件来删除，默认是 根据id来删除
     * @return
     * @author 张坤浩
     * @date
     */
    public int delete(Object id, String... params) throws SQLException {
        // 1.获取连接

        //1.定义sql语句:String sql = "DELETE FROM 表名 WHERE 条件;";
        StringBuffer sql = new StringBuffer("DELETE FROM ");

        //获取表名
        String name = getTableName().name();
        sql.append(name + " where ");

        //获取列的主键名=主键值
        //获取反射类的所有属性
        if (params != null && params.length == 0) {
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                //获取主键id的注解
                TableId tableId = field.getAnnotation(TableId.class);
                if (tableId != null) {
                    sql.append(tableId.value() + "='" + id + "'");
                }
            }
        } else {
            sql.append(params[0] + " = '" + params[1] + "'");
        }
        //System.out.println(sql);

        return getUpdateResult(sql.toString());
    }

    /**
     * 功能描述: 抽取删除、修改和插入数据的共同代码
     *
     * @param sql
     * @return
     * @author 张坤浩
     * @date
     */
    private int getUpdateResult(String sql) throws SQLException {
        // 1.获取连接
        PooledConnection pooledConnection = PoolManager.getInstance().getPooledConnection();
        PreparedStatement ps = null;
        Connection conn = pooledConnection.getConnection();
        //System.out.println("当前连接地址：" + pooledConnection.getConnection());

        //开启事务
        conn.setAutoCommit(false);

        //影响行数
        int row = 0;
        try {
            //2.获取PreparedStatement,用于执行sql
            ps = conn.prepareStatement(sql.toString());
            //3.执行sql
            row = ps.executeUpdate();

            //提交事务
            if (row > 0) {
                conn.commit();
            }

        } catch (Exception e) {
            //事务回滚
            conn.rollback();

            e.printStackTrace();
        } finally {
            //4.释放资源
            MyPoolImpl.free(pooledConnection, ps, null);
        }
        return row;
    }

    //增
    public int insert(T t, String... params) throws Exception {
        // 1.获取连接

        //1.定义sql语句: "INSERT INTO 表名 VALUES (值1, 值2, 值3, ...)"
        StringBuffer sql = new StringBuffer("Insert into ");

        //2.获取t的反射类
        Class<?> aClass = t.getClass();
        //3.获取类的注解
        TableName tableName = aClass.getAnnotation(TableName.class);
        //4.判断是否已经用注解标记表名
        if (tableName == null) {
            throw new RuntimeException("未使用TableName注解标记表名");
        }
        //5.获取表名
        String name = tableName.name();

        //6.判断是否是有指定列字段插入数据，还是一次性全部赋值
        if (params != null && params.length > 0) {
            //指定列字段插入数据
            sql.append(name + Arrays.toString(params).replace("[", "(").replace("]", ")") + " values ");
        } else {
            //一次性全部赋值
            sql.append(name + " values ");
        }

        //7.利用集合封装类的属性值
        List<Object> value = new ArrayList<>();
        //获取类的所有属性
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field field : declaredFields) {
            //打开私有成员变量的权限
            field.setAccessible(true);
            //获取属性值
            Object o = field.get(t);
            value.add("'" + o + "'");
        }
        //更换集合的括号
        String replace = value.toString().replace("[", "(").replace("]", ")");
        //拼接sql完成
        sql.append(replace);
        //System.out.println(sql);

        return getUpdateResult(sql.toString());
    }

    //改
    public int update(T t, String... params) throws Exception {
        // 1.获取连接
        PooledConnection pooledConnection = PoolManager.getInstance().getPooledConnection();
        Connection conn = pooledConnection.getConnection();
        //System.out.println("当前连接地址：" + pooledConnection.getConnection());

        //1.定义sql语句:String sql = "UPDATE 表名 SET 字段名=新值 WHERE 条件;";
        StringBuffer sql = new StringBuffer("UPDATE ");
        //2.获取t的反射类
        Class<?> aClass = t.getClass();
        //3.获取类的注解
        TableName tableName = aClass.getAnnotation(TableName.class);
        //4.判断是否已经用注解标记表名
        if (tableName == null) {
            throw new RuntimeException("未使用TableName注解标记表名");
        }
        //5.获取表名
        String name = tableName.name();

        sql.append(name + " set ");
        //获取所有列对象
        Field[] fields = aClass.getDeclaredFields();
        //拼接条件
        String where = " where ";

        for (Field field : fields) {
            field.setAccessible(true);
            //获取列上的主键注解
            TableId primaryKeyAnnotation = field.getAnnotation(TableId.class);
            if (primaryKeyAnnotation != null) {
                //是主键注解
                if (params.length == 0) {
                    //不额外增加查询条件，默认使用id查询，拼接查询条件
                    where += primaryKeyAnnotation.value() + " = " + field.get(t);
                } else {
                    //自定义查询条件，拼接查询条件
                    where += params[0] + " = '" + params[1] + "'";
                }
            } else {
                TableField fieldAnnotation = field.getAnnotation(TableField.class);
                if (fieldAnnotation != null) {
                    //加了注解的成员变量
                    sql.append(fieldAnnotation.value() + "='" + field.get(t) + "',");
                } else {
                    //没加注解的成员变量
                    sql.append(field.getName() + "='" + field.get(t) + "',");
                }
            }
        }
        //拼接完成
        String s = sql.substring(0, sql.lastIndexOf(","));
        s += where;
        //System.out.println(s);

        return getUpdateResult(s);
    }
}
